#include <iostream>
#include <sstream>
#include <grpcpp/grpcpp.h>

#include "remote_calculate.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using grpc::ClientWriter;

using calculator::Calculator;
using calculator::AddRequest;
using calculator::AddResponse;
using calculator::SumRequest;
using calculator::SumResponse;
using calculator::RankRequest;
using calculator::RankResponse;
using calculator::SortRequest;
using calculator::SortResponse;

class CalculatorClient {
private:
    std::unique_ptr<Calculator::Stub> stub_; // 客户端存根
public:
    CalculatorClient(std::shared_ptr<Channel> channel)
        : stub_(Calculator::NewStub(channel)) {}

    int64_t Remote_Call_Add(int32_t a, int32_t b) {
        AddRequest request;
        request.set_a(a);
        request.set_b(b);

        AddResponse response;

        ClientContext context;

        Status status = stub_->Add(&context, request, &response);

        if (status.ok()) {
            return response.result();
        } else {
            std::cout << status.error_code() << ": " << status.error_message() 
                      << std::endl;
            return -1;
        }
    }

    int64_t Remote_Call_Sum(std::vector<int32_t>& nums) {
        SumResponse response;
        ClientContext context;
        auto writer = stub_->Sum(&context, &response);
        SumRequest request;
        for (auto num : nums) {
            request.set_num(num);
            writer->Write(request);         
        }
        writer->WritesDone();   // 通知数据写完了

        Status status = writer->Finish();
        if (status.ok()) {
            return response.sum();
        } else {
            std::cout << status.error_code() << ": " << status.error_message()
                      << std::endl;
            return -1;
        }
    }

    std::vector<int32_t> Remote_Call_Rank(int32_t digit) {
        RankRequest request;
        request.set_digit(digit);
        ClientContext context;
        auto reader(stub_->Rank(&context, request));
        
        std::vector<int32_t> result;
        RankResponse response;
        while (reader->Read(&response)) {
            result.emplace_back(response.digit());
        }

        Status status = reader->Finish();
        if (!status.ok()) {
            std::cout << status.error_code() << ": " << status.error_message()
                      << std::endl;
        }
        return result;
    }

    void Remote_Call_Sort(std::vector<int32_t>& digits) {
        ClientContext context;
        auto readerwriter = stub_->Sort(&context);

        SortRequest request;
        for (auto digit : digits) {
            request.set_num(digit);
            readerwriter->Write(request);
        }
        readerwriter->WritesDone();

        SortResponse response;
        int index = 0;
        while (readerwriter->Read(&response)) {
            digits[index] = response.num();
            index++;
        }
        Status status = readerwriter->Finish();
        if (!status.ok()) {
            std::cout << status.error_code() << ": " << status.error_message()
                      << std::endl;
        }
    }
};

int main(int argc, char* argv[]) {
    CalculatorClient client(
        grpc::CreateChannel("localhost:43089", grpc::InsecureChannelCredentials())
    );

    // test: Add()
    int32_t a, b;
    std::cout << "Please input two Integers for Add(): ";
    std::cin >> a >> b;
    std::cout << "Result: " << client.Remote_Call_Add(a, b) << std::endl;

    // test: Sum()
    std::vector<int32_t> nums;
    std::cout << "Please input some Integers for Sum(): ";
    std::string input_str;
    std::getchar(); // 吃掉回车
    std::getline(std::cin, input_str);
    std::stringstream str_stream;
    str_stream << input_str;
    int32_t num;
    while (str_stream >> num) {
        nums.emplace_back(num);
    }
    std::cout << "Sum: " << client.Remote_Call_Sum(nums) << std::endl;

    // test: Rank()
    std::cout << "Please input one Integers for Rand(): ";
    int32_t digit;
    std::cin >> digit;
    auto res = client.Remote_Call_Rank(digit);
    std::cout << "Rank: ";
    for (auto d : res) std::cout << d << " ";
    std::cout << std::endl;

    // test: Sort()
    std::cout << "Please input some Integers for Sort(): ";
    std::getchar();
    input_str = "";
    str_stream.clear();
    std::getline(std::cin, input_str);
    str_stream << input_str;
    std::vector<int32_t> digits;
    while (str_stream >> num) {
        digits.emplace_back(num);
    }
    std::cout << "Sort: ";
    client.Remote_Call_Sort(digits);
    for (auto digit : digits) std::cout << digit << " ";
    std::cout << std::endl;
    return 0;
}
